home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Blender 2.49b / blender-2.49b-windows.exe / $_4_ / .blender / scripts / bpymodules / BPyTextPlugin.py < prev    next >
Text File  |  2009-08-31  |  23KB  |  815 lines

  1. """The BPyTextPlugin Module
  2.  
  3. Use get_cached_descriptor(txt) to retrieve information about the script held in
  4. the txt Text object.
  5.  
  6. Use print_cache_for(txt) to print the information to the console.
  7.  
  8. Use line, cursor = current_line(txt) to get the logical line and cursor position
  9.  
  10. Use get_targets(line, cursor) to find out what precedes the cursor:
  11.     aaa.bbb.cc|c.ddd -> ['aaa', 'bbb', 'cc']
  12.  
  13. Use resolve_targets(txt, targets) to turn a target list into a usable object if
  14. one is found to match.
  15. """
  16.  
  17. import bpy, sys, os
  18. import __builtin__, tokenize
  19. from Blender.sys import time
  20. from tokenize import generate_tokens, TokenError, \
  21.         COMMENT, DEDENT, INDENT, NAME, NEWLINE, NL, STRING, NUMBER
  22.  
  23. class Definition:
  24.     """Describes a definition or defined object through its name, line number
  25.     and docstring. This is the base class for definition based descriptors.
  26.     """
  27.     
  28.     def __init__(self, name, lineno, doc=''):
  29.         self.name = name
  30.         self.lineno = lineno
  31.         self.doc = doc
  32.  
  33. class ScriptDesc:
  34.     """Describes a script through lists of further descriptor objects (classes,
  35.     defs, vars) and dictionaries to built-in types (imports). If a script has
  36.     not been fully parsed, its incomplete flag will be set. The time of the last
  37.     parse is held by the time field and the name of the text object from which
  38.     it was parsed, the name field.
  39.     """
  40.     
  41.     def __init__(self, name, imports, classes, defs, vars, incomplete=False):
  42.         self.name = name
  43.         self.imports = imports
  44.         self.classes = classes
  45.         self.defs = defs
  46.         self.vars = vars
  47.         self.incomplete = incomplete
  48.         self.parse_due = 0
  49.     
  50.     def set_delay(self, delay):
  51.         self.parse_due = time() + delay
  52.  
  53. class ClassDesc(Definition):
  54.     """Describes a class through lists of further descriptor objects (defs and
  55.     vars). The name of the class is held by the name field and the line on
  56.     which it is defined is held in lineno.
  57.     """
  58.     
  59.     def __init__(self, name, parents, defs, vars, lineno, doc=''):
  60.         Definition.__init__(self, name, lineno, doc)
  61.         self.parents = parents
  62.         self.defs = defs
  63.         self.vars = vars
  64.  
  65. class FunctionDesc(Definition):
  66.     """Describes a function through its name and list of parameters (name,
  67.     params) and the line on which it is defined (lineno).
  68.     """
  69.     
  70.     def __init__(self, name, params, lineno, doc=''):
  71.         Definition.__init__(self, name, lineno, doc)
  72.         self.params = params
  73.  
  74. class VarDesc(Definition):
  75.     """Describes a variable through its name and type (if ascertainable) and the
  76.     line on which it is defined (lineno). If no type can be determined, type
  77.     will equal None.
  78.     """
  79.     
  80.     def __init__(self, name, type, lineno):
  81.         Definition.__init__(self, name, lineno)
  82.         self.type = type # None for unknown (supports: dict/list/str)
  83.  
  84. # Context types
  85. CTX_UNSET = -1
  86. CTX_NORMAL = 0
  87. CTX_SINGLE_QUOTE = 1
  88. CTX_DOUBLE_QUOTE = 2
  89. CTX_COMMENT = 3
  90.  
  91. # Python keywords
  92. KEYWORDS = ['and', 'del', 'from', 'not', 'while', 'as', 'elif', 'global',
  93.             'or', 'with', 'assert', 'else', 'if', 'pass', 'yield',
  94.             'break', 'except', 'import', 'print', 'class', 'exec', 'in',
  95.             'raise', 'continue', 'finally', 'is', 'return', 'def', 'for',
  96.             'lambda', 'try' ]
  97.  
  98. # Module file extensions
  99. MODULE_EXTS = ['.py', '.pyc', '.pyo', '.pyw', '.pyd']
  100.  
  101. ModuleType = type(__builtin__)
  102. NoneScriptDesc = ScriptDesc('', dict(), dict(), dict(), dict(), True)
  103.  
  104. _modules = {}
  105. _modules_updated = 0
  106. _parse_cache = dict()
  107.  
  108. def _load_module_names():
  109.     """Searches the sys.path for module files and lists them, along with
  110.     sys.builtin_module_names, in the global dict _modules.
  111.     """
  112.     
  113.     global _modules
  114.     
  115.     for n in sys.builtin_module_names:
  116.         _modules[n] = None
  117.     for p in sys.path:
  118.         if p == '': p = os.curdir
  119.         if not os.path.isdir(p): continue
  120.         for f in os.listdir(p):
  121.             for ext in MODULE_EXTS:
  122.                 if f.endswith(ext):
  123.                     _modules[f[:-len(ext)]] = None
  124.                     break
  125.  
  126. _load_module_names()
  127.  
  128. def _trim_doc(doc):
  129.     """Trims the quotes from a quoted STRING token (eg. "'''text'''" -> "text")
  130.     """
  131.     
  132.     l = len(doc)
  133.     i = 0
  134.     while i < l/2 and (doc[i] == "'" or doc[i] == '"'):
  135.         i += 1
  136.     return doc[i:-i]
  137.  
  138. def resolve_targets(txt, targets):
  139.     """Attempts to return a useful object for the locally or externally defined
  140.     entity described by targets. If the object is local (defined in txt), a
  141.     Definition instance is returned. If the object is external (imported or
  142.     built in), the object itself is returned. If no object can be found, None is
  143.     returned.
  144.     """
  145.     
  146.     count = len(targets)
  147.     if count==0: return None
  148.     
  149.     obj = None
  150.     local = None
  151.     i = 1
  152.     
  153.     desc = get_cached_descriptor(txt)
  154.     b = targets[0].find('(')
  155.     if b==-1: b = None # Trick to let us use [:b] and get the whole string
  156.     
  157.     if desc.classes.has_key(targets[0][:b]):
  158.         local = desc.classes[targets[0][:b]]
  159.     elif desc.defs.has_key(targets[0]):
  160.         local = desc.defs[targets[0]]
  161.     elif desc.vars.has_key(targets[0]):
  162.         obj = desc.vars[targets[0]].type
  163.     
  164.     if local:
  165.         while i < count:
  166.             b = targets[i].find('(')
  167.             if b==-1: b = None
  168.             if hasattr(local, 'classes') and local.classes.has_key(targets[i][:b]):
  169.                 local = local.classes[targets[i][:b]]
  170.             elif hasattr(local, 'defs') and local.defs.has_key(targets[i]):
  171.                 local = local.defs[targets[i]]
  172.             elif hasattr(local, 'vars') and local.vars.has_key(targets[i]):
  173.                 obj = local.vars[targets[i]].type
  174.                 local = None
  175.                 i += 1
  176.                 break
  177.             else:
  178.                 local = None
  179.                 break
  180.             i += 1
  181.     
  182.     if local: return local
  183.     
  184.     if not obj:
  185.         if desc.imports.has_key(targets[0]):
  186.             obj = desc.imports[targets[0]]
  187.         else:
  188.             builtins = get_builtins()
  189.             if builtins.has_key(targets[0]):
  190.                 obj = builtins[targets[0]]
  191.     
  192.     while obj and i < count:
  193.         if hasattr(obj, targets[i]):
  194.             obj = getattr(obj, targets[i])
  195.         else:
  196.             obj = None
  197.             break
  198.         i += 1
  199.     
  200.     return obj
  201.  
  202. def get_cached_descriptor(txt, force_parse=0):
  203.     """Returns the cached ScriptDesc for the specified Text object 'txt'. If the
  204.     script has not been parsed in the last 'period' seconds it will be reparsed
  205.     to obtain this descriptor.
  206.     
  207.     Specifying TP_AUTO for the period (default) will choose a period based on the
  208.     size of the Text object. Larger texts are parsed less often.
  209.     """
  210.     
  211.     global _parse_cache
  212.     
  213.     parse = True
  214.     key = hash(txt)
  215.     if not force_parse and _parse_cache.has_key(key):
  216.         desc = _parse_cache[key]
  217.         if desc.parse_due > time():
  218.             parse = desc.incomplete
  219.     
  220.     if parse:
  221.         desc = parse_text(txt)
  222.     
  223.     return desc
  224.  
  225. def parse_text(txt):
  226.     """Parses an entire script's text and returns a ScriptDesc instance
  227.     containing information about the script.
  228.     
  229.     If the text is not a valid Python script (for example if brackets are left
  230.     open), parsing may fail to complete. However, if this occurs, no exception
  231.     is thrown. Instead the returned ScriptDesc instance will have its incomplete
  232.     flag set and information processed up to this point will still be accessible.
  233.     """
  234.     
  235.     start_time = time()
  236.     txt.reset()
  237.     tokens = generate_tokens(txt.readline) # Throws TokenError
  238.     
  239.     curl, cursor = txt.getCursorPos()
  240.     linen = curl + 1 # Token line numbers are one-based
  241.     
  242.     imports = dict()
  243.     imp_step = 0
  244.     
  245.     classes = dict()
  246.     cls_step = 0
  247.     
  248.     defs = dict()
  249.     def_step = 0
  250.     
  251.     vars = dict()
  252.     var1_step = 0
  253.     var2_step = 0
  254.     var3_step = 0
  255.     var_accum = dict()
  256.     var_forflag = False
  257.     
  258.     indent = 0
  259.     prev_type = -1
  260.     prev_text = ''
  261.     incomplete = False
  262.     
  263.     while True:
  264.         try:
  265.             type, text, start, end, line = tokens.next()
  266.         except StopIteration:
  267.             break
  268.         except (TokenError, IndentationError):
  269.             incomplete = True
  270.             break
  271.         
  272.         # Skip all comments and line joining characters
  273.         if type == COMMENT or type == NL:
  274.             continue
  275.         
  276.         #################
  277.         ## Indentation ##
  278.         #################
  279.         
  280.         if type == INDENT:
  281.             indent += 1
  282.         elif type == DEDENT:
  283.             indent -= 1
  284.         
  285.         #########################
  286.         ## Module importing... ##
  287.         #########################
  288.         
  289.         imp_store = False
  290.         
  291.         # Default, look for 'from' or 'import' to start
  292.         if imp_step == 0:
  293.             if text == 'from':
  294.                 imp_tmp = []
  295.                 imp_step = 1
  296.             elif text == 'import':
  297.                 imp_from = None
  298.                 imp_tmp = []
  299.                 imp_step = 2
  300.         
  301.         # Found a 'from', create imp_from in form '???.???...'
  302.         elif imp_step == 1:
  303.             if text == 'import':
  304.                 imp_from = '.'.join(imp_tmp)
  305.                 imp_tmp = []
  306.                 imp_step = 2
  307.             elif type == NAME:
  308.                 imp_tmp.append(text)
  309.             elif text != '.':
  310.                 imp_step = 0 # Invalid syntax
  311.         
  312.         # Found 'import', imp_from is populated or None, create imp_name
  313.         elif imp_step == 2:
  314.             if text == 'as':
  315.                 imp_name = '.'.join(imp_tmp)
  316.                 imp_step = 3
  317.             elif type == NAME or text == '*':
  318.                 imp_tmp.append(text)
  319.             elif text != '.':
  320.                 imp_name = '.'.join(imp_tmp)
  321.                 imp_symb = imp_name
  322.                 imp_store = True
  323.         
  324.         # Found 'as', change imp_symb to this value and go back to step 2
  325.         elif imp_step == 3:
  326.             if type == NAME:
  327.                 imp_symb = text
  328.             else:
  329.                 imp_store = True
  330.         
  331.         # Both imp_name and imp_symb have now been populated so we can import
  332.         if imp_store:
  333.             
  334.             # Handle special case of 'import *'
  335.             if imp_name == '*':
  336.                 parent = get_module(imp_from)
  337.                 imports.update(parent.__dict__)
  338.                 
  339.             else:
  340.                 # Try importing the name as a module
  341.                 try:
  342.                     if imp_from:
  343.                         module = get_module(imp_from +'.'+ imp_name)
  344.                     else:
  345.                         module = get_module(imp_name)
  346.                 except (ImportError, ValueError, AttributeError, TypeError):
  347.                     # Try importing name as an attribute of the parent
  348.                     try:
  349.                         module = __import__(imp_from, globals(), locals(), [imp_name])
  350.                         imports[imp_symb] = getattr(module, imp_name)
  351.                     except (ImportError, ValueError, AttributeError, TypeError):
  352.                         pass
  353.                 else:
  354.                     imports[imp_symb] = module
  355.             
  356.             # More to import from the same module?
  357.             if text == ',':
  358.                 imp_tmp = []
  359.                 imp_step = 2
  360.             else:
  361.                 imp_step = 0
  362.         
  363.         ###################
  364.         ## Class parsing ##
  365.         ###################
  366.         
  367.         # If we are inside a class then def and variable parsing should be done
  368.         # for the class. Otherwise the definitions are considered global
  369.         
  370.         # Look for 'class'
  371.         if cls_step == 0:
  372.             if text == 'class':
  373.                 cls_name = None
  374.                 cls_lineno = start[0]
  375.                 cls_indent = indent
  376.                 cls_step = 1
  377.         
  378.         # Found 'class', look for cls_name followed by '(' parents ')'
  379.         elif cls_step == 1:
  380.             if not cls_name:
  381.                 if type == NAME:
  382.                     cls_name = text
  383.                     cls_sline = False
  384.                     cls_parents = dict()
  385.                     cls_defs = dict()
  386.                     cls_vars = dict()
  387.             elif type == NAME:
  388.                 if classes.has_key(text):
  389.                     parent = classes[text]
  390.                     cls_parents[text] = parent
  391.                     cls_defs.update(parent.defs)
  392.                     cls_vars.update(parent.vars)
  393.             elif text == ':':
  394.                 cls_step = 2
  395.         
  396.         # Found 'class' name ... ':', now check if it's a single line statement
  397.         elif cls_step == 2:
  398.             if type == NEWLINE:
  399.                 cls_sline = False
  400.             else:
  401.                 cls_sline = True
  402.             cls_doc = ''
  403.             cls_step = 3
  404.         
  405.         elif cls_step == 3:
  406.             if not cls_doc and type == STRING:
  407.                 cls_doc = _trim_doc(text)
  408.             if cls_sline:
  409.                 if type == NEWLINE:
  410.                     classes[cls_name] = ClassDesc(cls_name, cls_parents, cls_defs, cls_vars, cls_lineno, cls_doc)
  411.                     cls_step = 0
  412.             else:
  413.                 if type == DEDENT and indent <= cls_indent:
  414.                     classes[cls_name] = ClassDesc(cls_name, cls_parents, cls_defs, cls_vars, cls_lineno, cls_doc)
  415.                     cls_step = 0
  416.         
  417.         #################
  418.         ## Def parsing ##
  419.         #################
  420.         
  421.         # Look for 'def'
  422.         if def_step == 0:
  423.             if text == 'def':
  424.                 def_name = None
  425.                 def_lineno = start[0]
  426.                 def_step = 1
  427.         
  428.         # Found 'def', look for def_name followed by '('
  429.         elif def_step == 1:
  430.             if type == NAME:
  431.                 def_name = text
  432.                 def_params = []
  433.             elif def_name and text == '(':
  434.                 def_step = 2
  435.         
  436.         # Found 'def' name '(', now identify the parameters upto ')'
  437.         # TODO: Handle ellipsis '...'
  438.         elif def_step == 2:
  439.             if type == NAME:
  440.                 def_params.append(text)
  441.             elif text == ':':
  442.                 def_step = 3
  443.         
  444.         # Found 'def' ... ':', now check if it's a single line statement
  445.         elif def_step == 3:
  446.             if type == NEWLINE:
  447.                 def_sline = False
  448.             else:
  449.                 def_sline = True
  450.             def_doc = ''
  451.             def_step = 4
  452.         
  453.         elif def_step == 4:
  454.             if type == STRING:
  455.                 def_doc = _trim_doc(text)
  456.             newdef = None
  457.             if def_sline:
  458.                 if type == NEWLINE:
  459.                     newdef = FunctionDesc(def_name, def_params, def_lineno, def_doc)
  460.             else:
  461.                 if type == NAME:
  462.                     newdef = FunctionDesc(def_name, def_params, def_lineno, def_doc)
  463.             if newdef:
  464.                 if cls_step > 0: # Parsing a class
  465.                     cls_defs[def_name] = newdef
  466.                 else:
  467.                     defs[def_name] = newdef
  468.                 def_step = 0
  469.         
  470.         ##########################
  471.         ## Variable assignation ##
  472.         ##########################
  473.         
  474.         if cls_step > 0: # Parsing a class
  475.             # Look for 'self.???'
  476.             if var1_step == 0:
  477.                 if text == 'self':
  478.                     var1_step = 1
  479.             elif var1_step == 1:
  480.                 if text == '.':
  481.                     var_name = None
  482.                     var1_step = 2
  483.                 else:
  484.                     var1_step = 0
  485.             elif var1_step == 2:
  486.                 if type == NAME:
  487.                     var_name = text
  488.                     if cls_vars.has_key(var_name):
  489.                         var_step = 0
  490.                     else:
  491.                         var1_step = 3
  492.             elif var1_step == 3:
  493.                 if text == '=':
  494.                     var1_step = 4
  495.                 elif text != ',':
  496.                     var1_step = 0
  497.             elif var1_step == 4:
  498.                 var_type = None
  499.                 if type == NUMBER:
  500.                     close = end[1]
  501.                     if text.find('.') != -1: var_type = float
  502.                     else: var_type = int
  503.                 elif type == STRING:
  504.                     close = end[1]
  505.                     var_type = str
  506.                 elif text == '[':
  507.                     close = line.find(']', end[1])
  508.                     var_type = list
  509.                 elif text == '(':
  510.                     close = line.find(')', end[1])
  511.                     var_type = tuple
  512.                 elif text == '{':
  513.                     close = line.find('}', end[1])
  514.                     var_type = dict
  515.                 elif text == 'dict':
  516.                     close = line.find(')', end[1])
  517.                     var_type = dict
  518.                 if var_type and close+1 < len(line):
  519.                     if line[close+1] != ' ' and line[close+1] != '\t':
  520.                         var_type = None
  521.                 cls_vars[var_name] = VarDesc(var_name, var_type, start[0])
  522.                 var1_step = 0
  523.         
  524.         elif def_step > 0: # Parsing a def
  525.             # Look for 'global ???[,???]'
  526.             if var2_step == 0:
  527.                 if text == 'global':
  528.                     var2_step = 1
  529.             elif var2_step == 1:
  530.                 if type == NAME:
  531.                     if not vars.has_key(text):
  532.                         vars[text] = VarDesc(text, None, start[0])
  533.                 elif text != ',' and type != NL:
  534.                     var2_step == 0
  535.         
  536.         else: # In global scope
  537.             if var3_step == 0:
  538.                 # Look for names
  539.                 if text == 'for':
  540.                     var_accum = dict()
  541.                     var_forflag = True
  542.                 elif text == '=' or (var_forflag and text == 'in'):
  543.                     var_forflag = False
  544.                     var3_step = 1
  545.                 elif type == NAME:
  546.                     if prev_text != '.' and not vars.has_key(text):
  547.                         var_accum[text] = VarDesc(text, None, start[0])
  548.                 elif not text in [',', '(', ')', '[', ']']:
  549.                     var_accum = dict()
  550.                     var_forflag = False
  551.             elif var3_step == 1:
  552.                 if len(var_accum) != 1:
  553.                     var_type = None
  554.                     vars.update(var_accum)
  555.                 else:
  556.                     var_name = var_accum.keys()[0]
  557.                     var_type = None
  558.                     if type == NUMBER:
  559.                         if text.find('.') != -1: var_type = float
  560.                         else: var_type = int
  561.                     elif type == STRING: var_type = str
  562.                     elif text == '[': var_type = list
  563.                     elif text == '(': var_type = tuple
  564.                     elif text == '{': var_type = dict
  565.                     vars[var_name] = VarDesc(var_name, var_type, start[0])
  566.                 var3_step = 0
  567.         
  568.         #######################
  569.         ## General utilities ##
  570.         #######################
  571.         
  572.         prev_type = type
  573.         prev_text = text
  574.     
  575.     desc = ScriptDesc(txt.name, imports, classes, defs, vars, incomplete)
  576.     desc.set_delay(10 * (time()-start_time) + 0.05)
  577.     
  578.     global _parse_cache
  579.     _parse_cache[hash(txt)] = desc
  580.     return desc
  581.  
  582. def get_modules(since=1):
  583.     """Returns the set of built-in modules and any modules that have been
  584.     imported into the system upto 'since' seconds ago.
  585.     """
  586.     
  587.     global _modules, _modules_updated
  588.     
  589.     t = time()
  590.     if _modules_updated < t - since:
  591.         _modules.update(sys.modules)
  592.         _modules_updated = t
  593.     return _modules.keys()
  594.  
  595. def suggest_cmp(x, y):
  596.     """Use this method when sorting a list of suggestions.
  597.     """
  598.     
  599.     return cmp(x[0].upper(), y[0].upper())
  600.  
  601. def get_module(name):
  602.     """Returns the module specified by its name. The module itself is imported
  603.     by this method and, as such, any initialization code will be executed.
  604.     """
  605.     
  606.     mod = __import__(name)
  607.     components = name.split('.')
  608.     for comp in components[1:]:
  609.         mod = getattr(mod, comp)
  610.     return mod
  611.  
  612. def type_char(v):
  613.     """Returns the character used to signify the type of a variable. Use this
  614.     method to identify the type character for an item in a suggestion list.
  615.     
  616.     The following values are returned:
  617.       'm' if the parameter is a module
  618.       'f' if the parameter is callable
  619.       'v' if the parameter is variable or otherwise indeterminable
  620.     
  621.     """
  622.     
  623.     if isinstance(v, ModuleType):
  624.         return 'm'
  625.     elif callable(v):
  626.         return 'f'
  627.     else: 
  628.         return 'v'
  629.  
  630. def get_context(txt):
  631.     """Establishes the context of the cursor in the given Blender Text object
  632.     
  633.     Returns one of:
  634.       CTX_NORMAL - Cursor is in a normal context
  635.       CTX_SINGLE_QUOTE - Cursor is inside a single quoted string
  636.       CTX_DOUBLE_QUOTE - Cursor is inside a double quoted string
  637.       CTX_COMMENT - Cursor is inside a comment
  638.     
  639.     """
  640.     
  641.     l, cursor = txt.getCursorPos()
  642.     lines = txt.asLines(0, l+1)
  643.     
  644.     # FIXME: This method is too slow in large files for it to be called as often
  645.     # as it is. So for lines below the 1000th line we do this... (quorn)
  646.     if l > 1000: return CTX_NORMAL
  647.     
  648.     # Detect context (in string or comment)
  649.     in_str = CTX_NORMAL
  650.     for line in lines:
  651.         if l == 0:
  652.             end = cursor
  653.         else:
  654.             end = len(line)
  655.             l -= 1
  656.         
  657.         # Comments end at new lines
  658.         if in_str == CTX_COMMENT:
  659.             in_str = CTX_NORMAL
  660.         
  661.         for i in range(end):
  662.             if in_str == 0:
  663.                 if line[i] == "'": in_str = CTX_SINGLE_QUOTE
  664.                 elif line[i] == '"': in_str = CTX_DOUBLE_QUOTE
  665.                 elif line[i] == '#': in_str = CTX_COMMENT
  666.             else:
  667.                 if in_str == CTX_SINGLE_QUOTE:
  668.                     if line[i] == "'":
  669.                         in_str = CTX_NORMAL
  670.                         # In again if ' escaped, out again if \ escaped, and so on
  671.                         for a in range(i-1, -1, -1):
  672.                             if line[a] == '\\': in_str = 1-in_str
  673.                             else: break
  674.                 elif in_str == CTX_DOUBLE_QUOTE:
  675.                     if line[i] == '"':
  676.                         in_str = CTX_NORMAL
  677.                         # In again if " escaped, out again if \ escaped, and so on
  678.                         for a in range(i-1, -1, -1):
  679.                             if line[i-a] == '\\': in_str = 2-in_str
  680.                             else: break
  681.         
  682.     return in_str
  683.  
  684. def current_line(txt):
  685.     """Extracts the Python script line at the cursor in the Blender Text object
  686.     provided and cursor position within this line as the tuple pair (line,
  687.     cursor).
  688.     """
  689.     
  690.     lineindex, cursor = txt.getCursorPos()
  691.     lines = txt.asLines()
  692.     line = lines[lineindex]
  693.     
  694.     # Join previous lines to this line if spanning
  695.     i = lineindex - 1
  696.     while i > 0:
  697.         earlier = lines[i].rstrip()
  698.         if earlier.endswith('\\'):
  699.             line = earlier[:-1] + ' ' + line
  700.             cursor += len(earlier)
  701.         i -= 1
  702.     
  703.     # Join later lines while there is an explicit joining character
  704.     i = lineindex
  705.     while i < len(lines)-1 and lines[i].rstrip().endswith('\\'):
  706.         later = lines[i+1].strip()
  707.         line = line + ' ' + later[:-1]
  708.         i += 1
  709.     
  710.     return line, cursor
  711.  
  712. def get_targets(line, cursor):
  713.     """Parses a period separated string of valid names preceding the cursor and
  714.     returns them as a list in the same order.
  715.     """
  716.     
  717.     brk = 0
  718.     targets = []
  719.     j = cursor
  720.     i = j-1
  721.     while i >= 0:
  722.         if line[i] == ')': brk += 1
  723.         elif brk:
  724.             if line[i] == '(': brk -= 1
  725.         else:
  726.             if line[i] == '.':
  727.                 targets.insert(0, line[i+1:j]); j=i
  728.             elif not (line[i].isalnum() or line[i] == '_' or line[i] == '.'):
  729.                 break
  730.         i -= 1
  731.     targets.insert(0, line[i+1:j])
  732.     return targets
  733.  
  734. def get_defs(txt):
  735.     """Returns a dictionary which maps definition names in the source code to
  736.     a list of their parameter names.
  737.     
  738.     The line 'def doit(one, two, three): print one' for example, results in the
  739.     mapping 'doit' : [ 'one', 'two', 'three' ]
  740.     """
  741.     
  742.     return get_cached_descriptor(txt).defs
  743.  
  744. def get_vars(txt):
  745.     """Returns a dictionary of variable names found in the specified Text
  746.     object. This method locates all names followed directly by an equal sign:
  747.     'a = ???' or indirectly as part of a tuple/list assignment or inside a
  748.     'for ??? in ???:' block.
  749.     """
  750.     
  751.     return get_cached_descriptor(txt).vars
  752.  
  753. def get_imports(txt):
  754.     """Returns a dictionary which maps symbol names in the source code to their
  755.     respective modules.
  756.     
  757.     The line 'from Blender import Text as BText' for example, results in the
  758.     mapping 'BText' : <module 'Blender.Text' (built-in)>
  759.     
  760.     Note that this method imports the modules to provide this mapping as as such
  761.     will execute any initilization code found within.
  762.     """
  763.     
  764.     return get_cached_descriptor(txt).imports
  765.  
  766. def get_builtins():
  767.     """Returns a dictionary of built-in modules, functions and variables."""
  768.     
  769.     return __builtin__.__dict__
  770.  
  771.  
  772. #################################
  773. ## Debugging utility functions ##
  774. #################################
  775.  
  776. def print_cache_for(txt, period=sys.maxint):
  777.     """Prints out the data cached for a given Text object. If no period is
  778.     given the text will not be reparsed and the cached version will be returned.
  779.     Otherwise if the period has expired the text will be reparsed.
  780.     """
  781.     
  782.     desc = get_cached_descriptor(txt, period)
  783.     print '================================================'
  784.     print 'Name:', desc.name, '('+str(hash(txt))+')'
  785.     print '------------------------------------------------'
  786.     print 'Defs:'
  787.     for name, ddesc in desc.defs.items():
  788.         print ' ', name, ddesc.params, ddesc.lineno
  789.         print '   ', ddesc.doc
  790.     print '------------------------------------------------'
  791.     print 'Vars:'
  792.     for name, vdesc in desc.vars.items():
  793.         print ' ', name, vdesc.type, vdesc.lineno
  794.     print '------------------------------------------------'
  795.     print 'Imports:'
  796.     for name, item in desc.imports.items():
  797.         print ' ', name.ljust(15), item
  798.     print '------------------------------------------------'
  799.     print 'Classes:'
  800.     for clsnme, clsdsc in desc.classes.items():
  801.         print '  *********************************'
  802.         print '  Name:', clsnme
  803.         print ' ', clsdsc.doc
  804.         print '  ---------------------------------'
  805.         print '  Defs:'
  806.         for name, ddesc in clsdsc.defs.items():
  807.             print '   ', name, ddesc.params, ddesc.lineno
  808.             print '     ', ddesc.doc
  809.         print '  ---------------------------------'
  810.         print '  Vars:'
  811.         for name, vdesc in clsdsc.vars.items():
  812.             print '   ', name, vdesc.type, vdesc.lineno
  813.         print '  *********************************'
  814.     print '================================================'
  815.